<?php

class test_Matchers extends TestSet {


  function set() {
    $set = new TestSet();
    $set->methods['dummy'] = new TestMethod();
    $set->running = 'dummy';
    return $set;
  }

  ## BASIC ASSERTS
  function test_basic_true_assert_should_pass() {
    $ass = $this->set()->assert(true);
    $this->assert($ass->passed == true);
  }

  function test_basic_false_assert_should_fail() {
    $ass = $this->set()->assert(false);
    $this->assert($ass->passed == false);
  }

  function test_basic_assertion_has_nice_message() {
    $ass = $this->set()->assert(true, "my_message");
    $this->assert_match("Assertion is true", $ass->toHtml());
    $this->assert_match("my_message", $ass->toHtml());
  }

  function test_basic_assertion_failure_has_nice_message() {
    $ass = $this->set()->assert(false, "my_message");
    $this->assert_match("Assertion is NOT true", $ass->toHtml());
    $this->assert_match("my_message", $ass->toHtml());
  }


  ## FALSE EXPECTATION
  function test_false_should_pass_with_false_expectation() {
    $ass = $this->set()->assert_false(false);
    $this->assert($ass->passed == true);
  }

  function test_true_should_fail_with_false_expectation() {
    $ass = $this->set()->assert_false(true);
    $this->assert($ass->passed == false);
  }

  function test_basic_assert_false_failure_has_nice_message() {
    $ass = $this->set()->assert_false(true, "my_message");
    $this->assert_match("Assertion is true", $ass->toHtml());
    $this->assert_match("my_message", $ass->toHtml());
  }



  ## EQUALITY TEST
  function equal() {
    return $this->set()->assert_equal(39, 39, "my_message");
  }

  function unequal() {
    return $this->set()->assert_equal(39, 38);
  }

  function test_equal_numbers_should_pass_equality_test() {
    $this->assert($this->equal()->passed);
  }

  function test_equality_pass_should_have_a_nice_message() {
    $message = "<span class=\"Actual\">39</span> equals "
      . "<span class=\"Expected\">39</span>";
    $this->assert_match($message, $this->equal()->message);
    $this->assert_match("my_message", $this->equal()->toHtml());
  }

  function test_unequal_numbers_should_fail_equality_test() {
    $this->assert_false($this->unequal()->passed);
  }

  function test_equality_fail_should_have_a_nice_message() {
    $message = "<span class=\"Actual\">39</span> does not equal "
      . "<span class=\"Expected\">38</span>";
    $this->assert_match($message, $this->unequal()->toHtml());
  }


  ## ARRAYS MATCH
  function test_array_should_convert_nicely_to_string() {
    $a = array("bi", "bim", "bap");
    $expected = "['bi','bim','bap']";
    $this->assert_equal(TestSet::array_to_simple_string($a), $expected);
  }

  function test_tags_in_array_should_convert_to_html_entities() {
    $a = array("<br>");
    $expected = "['&lt;br&gt;']";
    $this->assert_equal(TestSet::array_to_simple_string($a), $expected);
  }

  function test_matching_arrays_should_match() {
    $a = array("bi", "bim", "bap");
    $b = array("bi", "bim", "bap");
    $message = "array matches <span class=\"Expected\">['bi','bim','bap']</span>";

    $assert = $this->set()->assert_array_match($a, $b);
    $this->assert($assert->passed);

    $this->assert_equal($message, $assert->message);
  }

  function test_mismatch_arrays_should_not_match() {
    $a = array("bi", "bim", "bap");
    $b = array("bi", "donkeykong", "bap");
    $message = "<span class=\"Expected\">['bi',<b>'bim'</b>,'bap']</span> does not match "
      . "<span class=\"Actual\">['bi',<b>'donkeykong'</b>,'bap']</span>";
    $assert = $this->set()->assert_array_match($a, $b);
    $this->assert_false($assert->passed);

    $this->assert_equal($message, $assert->message);
  }

  function test_arrays_of_different_size_should_not_match() {
    $a = array("bi");
    $b = array("bi", "bim", "bap");
    $message = "<span class=\"Expected\">['bi']</span> does not match "
      . "<span class=\"Actual\">['bi',<b>'bim'</b>,'bap']</span>";

    $assert = $this->set()->assert_array_match($a, $b);
    $this->assert_false($assert->passed);
    $this->assert_equal($assert->message, $message);
  }

  function test_mismatch_should_take_precedence() {
    $a = array("zorg");
    $b = array("bi", "bim", "bap");

    $assert = $this->set()->assert_array_match($a, $b);
    $this->assert_false($assert->passed);
    $this->assert_equal($assert->position, 0);
  }

  function test_mismatched_keys_should_fail() {
    $a = array("0"=>"a","1"=>"b");
    $b = array("0"=>"a","2"=>"b");

    $assert = $this->set()->assert_array_match($a, $b);
    $this->assert_false($assert->passed);
    $this->assert_equal($assert->position, 1);
  }



  
  ## VALID HTML
  function test_tag_list_is_made() {
    $html = '<div id="foo"><a><br/><img src="ev">hi</a>ho</div>';
    $expected = array('div id="foo"','a','br/','img src="ev"','/a','/div');
    $this->assert_array_match(TestSet::html_to_tag_list($html), $expected);
  }

  function test_highlight_code() {
    $expected = "&lt;div&gt;&lt;/div&gt;<b>&lt;/div&gt;</b>";
    $tag = "div";
    $position = 2;
    $html = "<div></div></div>";
    $highlighted = TestSet::highlight_tag($position, $html);

    $this->assert_equal($highlighted, $expected);
  }

  function test_valid_html_should_pass() {
    $html = "<div><div><a>hi</a>ho<a></a>hee</div>zorg</div>";
    $assert = $this->set()->assert_valid_html($html);
    $this->assert($assert->passed);

    $this->assert_match(htmlentities($html), $assert->toHtml());
    $this->assert_match("is valid HTML", $assert->message);
  }

  function test_improperly_nested_tags_are_caught() {
    $html = "</a><a>";
    $assert = $this->set()->assert_valid_html($html);
    $this->assert_false($assert->passed);
  
    $this->assert_match(htmlentities($html), $assert->message);
    $this->assert_match("has a closing &lt;a&gt; tag \(#1\) without "
      . "a corresponding opener", $assert->message);
  }

  function test_extra_closers_should_be_caught() {
    $html = "<div></div></div>";
    $assert = $this->set()->assert_valid_html($html);
    $this->assert_false($assert->passed);

    $this->assert_match("&lt;div&gt;&lt;/div&gt;<b>&lt;/div&gt;</b>", 
      $assert->message);
    $this->assert_match("has a closing &lt;div&gt; tag \(#3\) without "
      . "a corresponding opener", $assert->message);
  }

  function test_extra_openers_should_be_caught() {
    $html = "<span>";
    $assert = $this->set()->assert_valid_html($html);
    $this->assert_false($assert->passed);

    $this->assert_match(htmlentities($html), $assert->message);
    $this->assert_match("has an unclosed &lt;span&gt; tag \(#1\)", $assert->message);
  }

  function test_unclosed_tag_is_caught() {
    $html = "<span><div></span>";
    $assert = $this->set()->assert_valid_html($html);
    $this->assert_false($assert->passed);

    $this->assert_match(htmlentities($html), $assert->message);
    $this->assert_match("has a closing &lt;span&gt; tag \(#3\), but "
      . "a &lt;div&gt; needs to be closed.", $assert->message);
  }


  ## BASIC MATCH
  function match() {
    return $this->set()->assert_match("blue", 
      "i live in a blue house", "my_message");
  }

  function test_basic_matcher_should_pass() {
    $this->assert($this->match()->passed);
  }

  function test_matcher_should_give_nice_message() {
    $msg = "found <span class=\"Expected\">"
      . "blue</span> in <span class=\"Actual\">i live in "
      . "a blue house</span>";
    $this->assert_match($msg, $this->match()->message);
    $this->assert_match("my_message", $this->match()->toHtml());
  }




  ## FAILURE TO MATCH
  function mismatch() {
    return $this->set()->assert_match("red", 
      "i live in a blue house");
  }

  function test_basic_mismatch_should_fail() {
    $this->assert(!$this->mismatch()->passed);
  }

  function test_failed_matcher_should_give_nice_message() {
    $msg = "did not find <span class=\"Expected\">"
      . "red</span> in <span class=\"Actual\">i live in "
      . "a blue house</span>";
    $this->assert_match($msg, $this->mismatch()->message);
  }



  # NON-MATCH
  function nonmatch_pass() {
    return $this->set()->assert_does_not_match("red", 
      "i live in a blue house", "my_message");
  }

  function test_does_not_match_should_pass() {
    $this->assert($this->nonmatch_pass()->passed);
  }

  function test_does_not_match_should_give_nice_message() {
    $msg = "did not find <span class=\"Expected\">"
      . "red</span> in <span class=\"Actual\">i live in "
      . "a blue house</span>";
    $this->assert_match($msg, $this->nonmatch_pass()->message);
    $this->assert_match("my_message", $this->nonmatch_pass()->toHtml());
  }

  function test_should_fail_if_strings_match() {
    $assert = $this->set()->assert_does_not_match("blue", 
      "i live in a blue house");
    $this->assert(!$assert->passed);
  }


  # Match with HTML in the strings
  function test_matcher_should_use_html_entities() {
    $assert = $this->set()->assert_match(">", "");
    $this->assert_match("&gt;", $assert->message);
  }
}
